# HttpUtil 工具

import re
import json
import argparse
import shlex
import requests
from collections import namedtuple
from urllib.parse import urlparse, parse_qs

RequestsParams=namedtuple('RequestsParams',['fullurl','url','method','params','data','json','headers'])

def curl2requests(curlcmd):

    curlcmd = curlcmd.replace('--url','')
    
    # 初始化解析器
    parser = argparse.ArgumentParser(description='Parse curl command')
    parser.add_argument('command')
    # 添加 URL 参数
    parser.add_argument( 'url', help='URL to be fetched' )

    # 添加 data 参数
    parser.add_argument('--data', '-d', action='append', dest='datas', help='Data to include in the body of the request', required=False)

    # 添加头部参数
    parser.add_argument('--header', '-H', action='append', dest='headers', help='Headers to include in the request', required=False)

    # 添加表单数据
    parser.add_argument('--form', '-F', action='append', dest='forms', help='Form data', required=False)

    # 添加 HTTP 方法
    parser.add_argument('--request', '-X', dest='method', help='HTTP method to use', default='')

    # 添加 data-raw 参数
    parser.add_argument('--data-raw', action='append', dest='data_raws', help='Raw data to include in the body of the request', required=False)

    # 添加 insecure 参数
    parser.add_argument('--insecure', action='store_true', dest='insecure', help='Allow insecure server connections when using SSL')

    # 使用 shlex.split 将 curl 命令分割成参数列表
    args,unknow_args = parser.parse_known_args(shlex.split(curlcmd))

    # 将 curl的参数，转换为 requests参数
    return args2RequestsParams(args)


# 解析argsparams 得到RequestsParams
def args2RequestsParams(args):
    
    # 解析url，获得uri部分和query部分
    parsed_url = urlparse(args.url)
    reqUrl=''
    if args.url !='':
        reqUrl=f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"

    # 提取查询部分
    str_query = parsed_url.query

    # 获取method
    method1= args.method 

    # 获取原始的headers，格式是数组
    headers1 = args.headers
    # 初始化一个空字典来存储解析后的键值对
    reqHeaders = {}
    cookies2={}
    content_type=''
    # 遍历 headers1 数组并解析键值对
    if headers1 is not None:
        for header in headers1:
            # 使用 split 方法按冒号分隔键和值
            key2, value2 = header.split(':', 1)
            # 去掉键和值两端的空白字符，并将其添加到字典中
            reqHeaders[key2.strip()] = value2.strip()

            if key2.strip().lower() == 'cookie':
                cookie_pairs = value2.strip().split(';')
                # 遍历每个键值对
                for pair3 in cookie_pairs:
                    key3, value3 = pair3.split('=', 1)
                    cookies2[key3.strip()] = value3.strip()

            # 提取Content-Type 
            if key2.strip().lower() =='content-type':
                content_type = value2.strip()

    reqMethod = method1

    # 将queryString转换为requests格式的
    reqParam={k5: v5[0] if len(v5)==1 else v5 \
              for k5, v5 in parse_qs(str_query,keep_blank_values=True).items()}

    # 读取 curl解析后的参数中的 data  form  data-raw 
    datas1 = args.datas
    forms1 = args.forms
    data_raws1 = args.data_raws

    # 根据 content_type 的值 和 method1 判断 reqMethod 
    if method1 != '':
        reqMethod = method1
    else:
        # 如果没有参数，则只要有 data form 或 data_raw 就是post，否则就是GET
        if datas1 is None and forms1 is None and data_raws1 is None: 
            reqMethod="GET"
        else: 
            reqMethod="POST"

    bodytype=None

    # reqMethod 是 post
    if reqMethod in ['POST','PUT','DELETE']:

        if content_type =='':
            if forms1 is None and datas1 is None and data_raws1 is None:
                bodytype=None
            if forms1 is not None and datas1 is None and data_raws1 is None:
                bodytype='form'
            else:
                bodytype='urlencoded'

        elif 'multipart/form-data' in content_type.lower():
            bodytype='form'
        elif 'application/x-www-form-urlencoded' in content_type.lower():
            bodytype='urlencoded'
        elif 'application/json' in content_type.lower() or 'application/xml' in content_type.lower() \
            or 'application/javascript' in content_type.lower() or 'text/plain' in content_type.lower() \
            or 'text/html' in content_type.lower() :
            bodytype='raw'




    # =====================  后面比较难，要分析 data  data-raw 和 form 这几个参数，
    # 最终确定 requests 的 data 和 json 的值
    #    post/put/delete 方式采用考虑这些参数
    #    data 和  data-raw 存在的时候  form 参数不生效
    #    form 的参数，最终生成 requests 的 data 参数
    #    form 参数，只允许字符串键值对
    #    data 和 data-raw 是一样的，将args的两者合并成一个数组即可
    #    data data-raw 优先级高于 form
    #    data 和  data-raw 全部为键值对的时候，以键值对方式合并
    #    data 和  data-raw 中有一个JSON格式的时候，拼接成一个长的字符串，成为raw参数
    #    bodytype 是 none ，所有参数都无效，即 requests.data 和 requests.json 都为 None
    #    bodytype 是 form ，先取 data 和 data-raws 的合集，再取 form ，
    #    bodytype 是 urlencoded ，也是取data 和 data-raws 的合集 ，再取 form
    #    注意：formdata 和 urlencoded 中， 如果参数中有 json格式的，则所有参数都无效，因为都被拼接成 raw 参数
    #    bodytype 是 raw, 只取 data 和 dataraw , 将所有字符串合并为一个大字符串即可
            
    # print(f"bodytype === {bodytype}")
    # print(f"Args Forms === {args.forms}")
    # print(f"Args Datas === {args.datas}")
    # print(f"Args DataRaws === {args.data_raws}")

    # 检查forms 参数中是否存在不是 键值对格式的字符串 
    # 检查到，就从参数列表中剔除
    if args.forms is not None:
        args.forms = [item for item in args.forms if isKeyValueStr(item) ]
            

    reqData = None
    reqJson = None
    if bodytype == 'form':
        # form 的暂时不做
        print(f'暂时未实现 bodytype== form方式 的请求，如果需要请联系开发人员进行程序优化。')
    elif bodytype=='urlencoded' :
        argsData = []
        # 如果只有 --form 参数
        if args.datas is None and args.data_raws is None:
            if args.forms is not None:
                argsData = argsData+ args.forms
        else:
            argsData = argsData + ( args.datas if args.datas is not None else [] )
            argsData = argsData + ( args.data_raws if args.data_raws is not None else [] )
            
        # 由于是urlencoding ，只取 键值对的参数
        argsData = [item for item in argsData if isKeyValueStr(item)]
        
        # 将 argsData 的键值对参数转为 dict 
        if len(argsData) >0:
            reqData = kvarray2Json(argsData)
    elif bodytype=='raw':
        # raw 将参数拼接为字符串， form参数无效
        rawVal=''
        if args.datas is not None:
            for str1 in args.datas:
                rawVal=f"{rawVal}&{str1}" if rawVal!='' else str1

        if args.data_raws is not None:
            for str1 in args.data_raws:
                rawVal=f"{rawVal}&{str1}" if rawVal!='' else str1

        if isJson(rawVal):
            reqJson=json.loads(rawVal)
        else:
            reqData=rawVal
                
    return RequestsParams(
        fullurl=args.url,
        url=reqUrl,
        method=reqMethod,
        headers=reqHeaders,
        params=reqParam,
        data=reqData,
        json=reqJson
    )




# 是否键值对的字符串 例如 abc=123
def isKeyValueStr(str):
    pattern = r'^.+=.*$'
    match = re.match(pattern, str)
    return match is not None  

def isJson(str):
    try:
        json.loads(str)
        return True
    except:
        return False

# 将 key value 数组，转换为json格式
# 例如 ["abc=12","bef=13&cdf=434"]
def kvarray2Json(kvarr):
    dict1 = {}
    for strkvPair in kvarr:
        arrstrkvPair = strkvPair.split('&')
        for strkvPair2 in arrstrkvPair:
            if strkvPair2 != '':
                # 如果莫名其妙造成字符串中没有 = 字符
                if '=' not in strkvPair2:
                    dict1[strkvPair2]=''
                else:
                    [k3,v3] =strkvPair2.split('=',1)
                    dict1[k3]=v3
    return dict1

def bytesFromUrl(imgurl):
    resp1=requests.get(imgurl)
    return resp1.content


        
        


